use strict;
use warnings;

package My::Form::Helper::Tools;
use base qw(Exporter);
our @EXPORT_OK =
    qw(eval_code evalable_code_from_hash eval_value flatten_form_elements init_fields save_fields set_html_attributes_on_RHTML save_to_hash_from_RHTML_form save_fields)
    ;    # symbols to export on request

sub eval_code {
    my %params        = @_;
    my $evalable_code = $params{evalable_code};
    my %save_to;
    my $rv = eval join( "\n", @{$evalable_code} );
    if ($@) {
        die "Code:\n" . join( "\n", @{$evalable_code} ) . "\nError:\n" . $@;
    }
    return $rv, %save_to;
} ## end sub eval_code

sub evalable_code_from_hash {
    my %params = @_;
    my @tokens;
    foreach my $key ( keys(%params) ) {
        if ( !$key || $key !~ /^[\_a-zA-Z]/ || $key =~ /\s/ ) {
            next;
        }
        my $value = $params{$key};
        push @tokens, qq/my \$$key = '$value';/;
    } ## end foreach my $key ( keys(%params) )
    return join( "\n", @tokens );
} ## end sub evalable_code_from_hash

sub eval_value {
    my %params          = @_;
    my %init_values     = @{ $params{init_values} };
    my $value           = $params{value};
    my $session_hashref = $params{session_hashref};
    if ( !$value || !ref($value) ) {
        return $value;
    } elsif ( ref($value) eq 'HASH' && exists( $value->{perl} ) ) {
        my $init_code = join( "\n", 'no strict "vars";', evalable_code_from_hash(%init_values) );
        my ( $rv, %save_to ) = eval_code( evalable_code => [ $init_code, $value->{perl} ] );
        if ( scalar( keys(%save_to) ) && $session_hashref ) {
            foreach my $key ( keys(%save_to) ) {
                $session_hashref->{$key} = $save_to{$key};
            }
        }
        return $rv;
    } ## end elsif ( ref($value) eq 'HASH' && exists( $value...
} ## end sub eval_value

sub flatten_form_elements {
    my %params   = @_;
    my $elements = $params{elements};
    my @element_list;
    foreach my $element_params ( @{$elements} ) {
        if ( exists( $element_params->{field_name} ) ) {
            push @element_list, $element_params;
        } else {
            if ( exists( $element_params->{fieldset} ) && ( $element_params->{fieldset} ) ) {

                # initialise the parameters we'd need for a nested call
                my %nested_params;
                $nested_params{elements} = $element_params->{elements};
                my @nested_element_list = flatten_form_elements(%nested_params);
                push @element_list, @nested_element_list;
            } ## end if ( exists( $element_params->{fieldset} )...
        } ## end else [ if ( exists( $element_params->{field_name}...
    } ## end foreach my $element_params ( @{$elements} )
    return @element_list;
} ## end sub flatten_form_elements

sub init_fields {
    my %params          = @_;
    my $RHTMLO_form     = $params{RHTMLO_form};
    my $cgi             = $params{cgi};
    my $init_from_cgi   = $params{init_from_cgi};
    my $session_hashref = $params{session_hashref};
    my @elements        = flatten_form_elements( elements => $params{elements} );
    $RHTMLO_form->clear();
    init_fields_from_session( RHTMLO_form     => $RHTMLO_form,
                              elements        => \@elements,
                              session_hashref => $session_hashref
    );
    $RHTMLO_form->init_fields_with_cgi( $cgi, no_clear => 1 ) if ( $cgi && $init_from_cgi );
} ## end sub init_fields

sub init_fields_from_session {
    my %params          = @_;
    my $RHTMLO_form     = $params{RHTMLO_form};
    my $session_hashref = $params{session_hashref};
    my @elements        = flatten_form_elements( elements => $params{elements} );
    foreach my $element_params (@elements) {
        if ( exists( $element_params->{field_name} ) ) {
            my $save_to =
                eval_value( init_values => [ %{$session_hashref} ], value => $element_params->{save_to} );
            my $session_value = $session_hashref->{$save_to} if ($save_to);
            if ( defined($session_value) ) {
                $RHTMLO_form->field( $element_params->{field_name} )->input_value($session_value);
            }
        } ## end if ( exists( $element_params->{field_name}...
    } ## end foreach my $element_params (@elements)
} ## end sub init_fields_from_session

sub save_fields {
    my %params          = @_;
    my $RHTMLO_form     = $params{RHTMLO_form};
    my $elements        = $params{elements};
    my $session_hashref = $params{session_hashref};
    my %save_to = save_to_hash_from_RHTML_form( RHTMLO_form     => $RHTMLO_form,
                                                elements        => $elements,
                                                session_hashref => $session_hashref
    );
    foreach my $key ( keys(%save_to) ) {
        $session_hashref->{$key} = $save_to{$key};
    }
} ## end sub save_fields

sub save_to_hash_from_RHTML_form {
    my %params           = @_;
    my $RHTMLO_form      = $params{RHTMLO_form};
    my @elements         = flatten_form_elements( elements => $params{elements} );
    my $session_hashref  = $params{session_hashref};
    my $validated_fields = $params{validated_fields};
    my %field_session_map;
    my %save_to;
    foreach my $element_params (@elements) {

        if (    exists( $element_params->{field_name} )
             && exists( $element_params->{save_to} )
             && ( ( defined( $element_params->{condition} )
                    && eval_value( init_values => [ %{$session_hashref} ],
                                   value       => $element_params->{condition} )
                  )
                  || ( !defined( $element_params->{condition} ) )
             )
            )
        {
            my $value;
            if ( !$validated_fields
                 || ( $validated_fields && $RHTMLO_form->field( $element_params->{field_name} )->validate ) )
            {

                # only use this value if it passes validation
                $value = $RHTMLO_form->field( $element_params->{field_name} )->internal_value;
            } ## end if ( !$validated_fields || ( $validated_fields...
            $save_to{ eval_value( init_values => [ %{$session_hashref} ],
                                  value => $element_params->{save_to} ) } = $value;
        } ## end if ( exists( $element_params->{field_name}...
    } ## end foreach my $element_params (@elements)
    return %field_session_map, %save_to;
} ## end sub save_to_hash_from_RHTML_form

sub set_html_attributes_on_RHTML {
    my ( $RHTMLO, %attributes ) = @_;
    $RHTMLO->validate_html_attrs(0);
    foreach my $attribute_name ( keys(%attributes) ) {
        $RHTMLO->html_attr( $attribute_name, $attributes{$attribute_name} );
    }
} ## end sub set_html_attributes_on_RHTML
1;
